﻿using System.IO;
using Ast.Framework.Environment.Extensions.Models;
using Ast.Framework.FileSystems.Dependencies;
using Ast.Framework.FileSystems.Paths;
using Ast.Framework.FileSystems.SiteFolders;
using Castle.Core.Logging;

namespace Ast.Framework.Environment.Extensions.Loaders
{
    /// <summary>
    /// Load an extension by looking through the BuildManager referenced assemblies
    /// </summary>
    public class ReferencedExtensionLoader : ExtensionLoaderBase
    {
        private readonly IBuildManager _buildManager;
        private readonly IPathProvider _pathProvider;
        private readonly ISiteFolderRoot _siteFolderRoot;

        public ReferencedExtensionLoader(IBuildManager buildManager, IPathProvider pathProvider, ISiteFolderRoot siteFolderRoot)
        {
            _buildManager = buildManager;
            _pathProvider = pathProvider;
            _siteFolderRoot = siteFolderRoot;

            Logger = NullLogger.Instance;
        }

        public ILogger Logger { get; set; }

        public override int Order
        {
            get { return 20; }
        }

        public override void ExtensionDeactivated(ExtensionLoadingContext ctx, ExtensionDescriptor extension)
        {
            DeleteAssembly(ctx, extension.Id);
        }

        public override void ExtensionRemoved(ExtensionLoadingContext ctx, DependencyDescriptor dependency)
        {
            DeleteAssembly(ctx, dependency.Name);
        }

        private void DeleteAssembly(ExtensionLoadingContext ctx, string moduleName)
        {
            var assemblyPath = _pathProvider.Combine(_siteFolderRoot.Path, moduleName + ".dll");
            if (_pathProvider.FileExists(assemblyPath))
            {
                ctx.DeleteActions.Add(() =>
                {
                    Logger.InfoFormat("ExtensionRemoved: Deleting assembly \"{0}\" from bin directory", moduleName);
                    _pathProvider.DeleteFile(assemblyPath);
                });
                ctx.RestartAppDomain = true;
            }
        }

        public override ExtensionProbeEntry Probe(ExtensionDescriptor descriptor)
        {
            var assembly = _buildManager.GetReferencedAssembly(descriptor.Id);
            if (assembly == null)
                return null;

            var assemblyPath = _pathProvider.Combine(_siteFolderRoot.Path, descriptor.Id + ".dll");

            return new ExtensionProbeEntry
            {
                Descriptor = descriptor,
                Loader = this,
                Priority = 100, // Higher priority because assemblies in ~/bin always take precedence
                Path = assemblyPath,
                PathDependencies = new[] {assemblyPath},
            };
        }

        public override ExtensionEntry Load(ExtensionDescriptor descriptor)
        {
            var assembly = _buildManager.GetReferencedAssembly(descriptor.Id);
            if (assembly == null)
                return null;

            Logger.InfoFormat("Loaded referenced extension \"{0}\": assembly name=\"{1}\"", descriptor.Name, assembly.FullName);

            return new ExtensionEntry
            {
                Descriptor = descriptor,
                Assembly = assembly,
                ExportedTypes = assembly.GetExportedTypes()
            };
        }
    }
}